/**
 *  @file CFString.cpp
 */
#ifdef WIN32
#include <windows.h>
#endif
#include "../common/CppMemPool.h"
#include "../common/UtilFuncT.h"
#include "CFString.h"
#include "UTFCharConv.h"
#include "../exceptions/OutOfBoundsException.h"
#include <stdio.h>
#include <stdarg.h>

namespace cppflib 
{

//! when reallocate a string buffer, it will be block-aligned to this value
#define STRBLOCK_ALGIN	8
//! size of string length field
#define SZ_LEN	sizeof(int)
//! size of string buffer size field
#define SZ_SIZE sizeof(int)

//! space reserved for length and size
#define NRCHAR_RESERVED ((SZ_LEN + SZ_SIZE) / sizeof(wc_t))



//! To set a string as an internal string
//! "Internal" strings are those returned by functions with 
//! return type "CFString" (not a reference by a real instance)
#define SET_INTERNAL_STRING(attrib) attrib |= 0x01
#define IS_INTERNAL_STRING(attrib) (attrib & 0x01)

//! align to 4 byte boundary
#define ALIGNEDCHAR (sizeof(int) / sizeof(wc_t))

/**
 *  Return aligned size (4 byte or 2 char aligned)
 */
PRIVATE int __GetCharAligned(int nrChar)
{
   int s = nrChar % ALIGNEDCHAR;
   return s ? (nrChar - s + ALIGNEDCHAR) : nrChar;
}

/**
 *  Get enough string length plus reserved space to store length and size
 *
 *  @param [in] orgLen -- original length in chars
 */
PRIVATE int __GetStrLenReserved(int orgLen)
{
   // include terminating null
   return __GetCharAligned(orgLen + 1) + NRCHAR_RESERVED;
}

/**
 *  Write length and size of this CFString at the end of the buffer
 *
 *  It is declared static and private so as to access internal members of CFString
 */
void CFString::__WriteStrLenAndSize(CFString *pStr)
{
   int * pInt = reinterpret_cast<int*>(pStr->m_szString + __GetCharAligned(pStr->m_nLen + 1)); // include terminating null
   *pInt++ = pStr->m_nLen;
   *pInt = pStr->m_nSize;
}

/**
 *  Retrieve length and size of this CFString at the end of the buffer
 *
 *  @param [out] nLen -- length
 *  @param [out] nSize -- size 
 */
PRIVATE void __RetStrLenAndSize(pcwstr_t pStr, int *nLen, int *nSize)
{
   int * pInt = reinterpret_cast<int*>(const_cast<pwstr_t>(pStr + __GetCharAligned(CFString::WStrlen(pStr) + 1))); // include terminating null
   *nLen = *pInt++;
   *nSize = *pInt;
}

/******************************* CFString **********************************/

/**
 *  @brief Reallocate/Allocate string buffer if there is not enough.
 *
 *  Allocate the buffer to be block aligned (STRBLOCK_ALGIN) (if realloc)
 *
 *  @note will update "m_nSize"
 *
 *  @param[in] requestSize -- request size in character
 *
 *  @return 0 -- OK
 *  @return < 0 -- error
 */
int CFString::ReAllocBuffer(int requestSize)
{   
   int sizeToAlloc = requestSize;

   //
   //  Block-align it if reallocate (i.e. to allocate a few more characters)
   //+ Since once reallocation (adding characters) is performed, it is 
   //+ likely that more characters will be added.
   //
   if (this->m_szString) {
      sizeToAlloc = requestSize / STRBLOCK_ALGIN;
      sizeToAlloc = (sizeToAlloc + 1) * STRBLOCK_ALGIN; // always allocate 1 more block
      this->m_szString = static_cast<pwstr_t>(::MRP_Realloc(this->m_szString, sizeToAlloc * sizeof(wc_t)));
   }
   //  if string is already NULL
   else {
      // don't add to mem pool because the pointer to CFString is already being added
      this->m_szString = static_cast<pwstr_t>(::MRP_Calloc(sizeToAlloc, sizeof(wc_t)));
   }  

   this->m_nSize = sizeToAlloc;

   if (!this->m_szString)
      return -1; 
   
   return 0;
}


/**
 *  All constructor should call this at the very beginning
 */
void CFString::InitInternalMember(void)
{
   // member variable
   m_szString = NULL;
   m_nLen = 0;
   m_nSize = 0;
   m_attrib = 0;
}

/**
 *  Used in constructor and operator "=", "+=" to initialize with a UTF8 string
 *
 *  @param[in] psz -- pointer to UTF8 character string
 *  @param[in] nLen -- length of psz
 *  @param[in] nSize -- size requested in chars
 *  @param[in] isAppend -- indicates if "psz" is appended to current string or not
 *
 *  @return true -- ok
 */
bool CFString::InitUTF8ToWideString(const char * psz, int nLen, int nSize, bool isAppend)
{
   int sizeRequest, sizeNeedInChar;

   // check size needed
   if (psz) {
      sizeNeedInChar = cppflib::text::UTF8ToUTF16(psz, nLen, NULL, 0);      
   }
   else {
      // set to 0 if NULL
      sizeNeedInChar = 0;
   }

   //  determine size request
   if (isAppend)
      sizeRequest = cppflib::Max<int>(nSize, m_nLen + sizeNeedInChar + 1);
   else 
      sizeRequest = cppflib::Max<int>(nSize, sizeNeedInChar + 1);
   
   // allocate buffer
   if (m_nSize < sizeRequest) {
      // will update m_nSize as well
      ReAllocBuffer(sizeRequest);
   }
   

   // actual conversion
   if (psz) {
      pwstr_t pTempString = (isAppend) ? (m_szString + m_nLen) : m_szString;
      cppflib::text::UTF8ToUTF16(psz, nLen, pTempString, sizeNeedInChar);
   }

   //  post processing
   if (isAppend)
      m_nLen += sizeNeedInChar;
   else 
      m_nLen = sizeNeedInChar;
   m_szString[m_nLen] = _S('\0');
      
   return true;
}

/**
 *  Used in constructor and operator "=", "+=" to initialize with a wide
 *  character string.
 *
 *  @param[in] lpsz -- pointer to unicode character string
 *  @param[in] nLen -- length of lpsz
 *  @param[in] nSize -- size requested in chars
 *  @param[in] isAppend -- indicates if "lpsz" is appended to current string or not
 *
 *  @return true -- ok
 */
bool CFString::InitUTF16ToWideString(pcwstr_t lpsz, int nLen, int nSize, bool isAppend)
{
   int sizeRequest;

   //  determine size request
   if (isAppend)
      sizeRequest = cppflib::Max<int>(nSize, m_nLen + nLen + 1);
   else
      sizeRequest = cppflib::Max<int>(nSize, nLen + 1);
   
   // allocate buffer
   if (m_nSize < sizeRequest) {
      // will update m_nSize as well
      ReAllocBuffer(sizeRequest);
   }
   
   
   // copy
   if (lpsz) {
      pwstr_t pTempString = (isAppend) ? (m_szString + m_nLen) : m_szString;
      CFString::Memcpy(pTempString, lpsz, nLen * sizeof(wc_t));
   }

   //  post processing
   if (isAppend) 
      m_nLen += nLen;
   else 
      m_nLen = nLen;
   m_szString[m_nLen] = _S('\0');
   
   return 0;
}

/**
 *  Default constructor
 */
CFString::CFString(void)
{
   InitInternalMember();
//TRACE(_S("default ctor\r\n"));

}

/**
 *  Initialize with a UTF8 string.
 *
 *  @param[in] psz -- null-terminated char array
 */
CFString::CFString(const char * psz)
{
   InitInternalMember();
   
   int len = static_cast<int>(CFString::Strlen(psz));
   InitUTF8ToWideString(psz, len, 0);
}

/**
 *  Initialize with a UTF8 string.
 *
 *  @param[in] psz -- char array (not necessary null-terminated)
 *  @param[in] nLen -- length of psz
 *  @param[in] nSize -- size of buffer (wchar_t not byte) to allocate,
 *                      can be greater than length
 */
CFString::CFString(const char * psz, int nLen, int nSize)
{
   InitInternalMember();
   InitUTF8ToWideString(psz, nLen, nSize);
}

/**
 *  Initialize with a wide character string.
 *
 *  @param[in] lpsz -- null-terminated wide char array
 */
CFString::CFString(pcwstr_t lpsz)
{
   InitInternalMember();

   int len = static_cast<int>(CFString::WStrlen(lpsz));
   InitUTF16ToWideString(lpsz, len, 0);
}

/**
 *  Initialize with a wide character string.
 *
 *  @param[in] lpsz -- wide char array (not necessary null-terminated)
 *  @param[in] nLen -- length of lpsz
 *  @param[in] nSize -- size of buffer (wchar_t not byte) to allocate,
 *                      can be greater than length
 */
CFString::CFString(pcwstr_t lpsz, int nLen, int nSize)
{
   InitInternalMember();
   InitUTF16ToWideString(lpsz, nLen, nSize);
}

/** 
 *  Initialize with another "CFString". A "size" can be specified
 *  to allocate more buffer.
 *
 *  @param[in] szSrc -- reference to another "CFString"
 *  @param[in] nSize -- size of buffer (wchar_t not byte) to allocate
 */
CFString::CFString(const CFString &szSrc, int nSize)
{
   InitInternalMember();
   InitUTF16ToWideString(szSrc.operator pcwstr_t(), szSrc.GetLength(), nSize);
}

/**
 *  Private constructor for internal use
 *
 *  @note At the end of the string (immediately after '\0') would 
 *        contain the length and size of the string.
 *
 *        <pre>
 *        Therefore, "lpsz" will at least contains
 *
 *              +------------------------+
 *              | \\0 | \\0 | len | size |
 *              +------------------------+
 *        </pre>
 *
 *  @note This "size" appended will include space used by 
 *        "len" and "size" itself. 
 *
 *  @param[in] lpsz -- actually it is a wide character string but explicitly
 *                     casted to "const void *".
 */
CFString::CFString(const void * lpsz)
{   
   InitInternalMember();
//TRACE(_S("void -- %s|\r\n"), (pwstr_t)lpsz);

   SET_INTERNAL_STRING(m_attrib);

   // attach string
   Attach(static_cast<pwstr_t>(const_cast<void*>(lpsz)));
   
}

/**
 *  @brief Another private constructor.
 *
 *  It is just the same as the default constructor except
 *  the attribute is set to internal string.
 *
 *  @param[in] lpsz -- Not used. Pass NULL is OK.
 *  
 */
CFString::CFString(void * lpsz)
{
   InitInternalMember();
   SET_INTERNAL_STRING(m_attrib);
}

/**
 *  @brief Destructor
 */
CFString::~CFString(void)
{
   // free string
   if (m_szString) {
      ::MRP_Release(m_szString);         
   }
   m_szString = NULL;
   m_nLen = 0;
   m_nSize = 0;
   m_attrib = 0;

//TRACE(_S("d'tor\r\n"));
}

/*************************** normal operations **************************/

/**
 *  Compare string with case sensitivity
 *
 *  @param[in] szString -- string to compare
 *
 *  @return  same as "strcmp"
 *
 *  @note if both string are NULL, 0 is returned.
 *        if this string object is NULL, less than 0 is returned.
 *        if szString is NULL, greater than 0 is returned.
 */
int CFString::Compare(pcwstr_t szString) const
{
   if (!m_szString && !szString) return 0;
   else if (!m_szString) return -1;
   else if (!szString) return 1;

   return CFString::WStrcmp(m_szString, szString);
}

/**
 *  Compare string without case sensitivity
 *
 *  @param[in] szString -- string to compare
 *
 *  @return  same as "stricmp"
 *
 *  @note if both string are NULL, 0 is returned.
 *        if this string object is NULL, less than 0 is returned.
 *        if szString is NULL, greater than 0 is returned.
 */
int CFString::CompareNoCase(pcwstr_t szString) const
{
   if (!m_szString && !szString) return 0;
   else if (!m_szString) return -1;
   else if (!szString) return 1;

   return CFString::WStricmp(m_szString, szString);
}

/**
 *  @brief Delete characters starting from a specified position
 *  
 *  Actually, the characters are not deleted but only the characters
 *  at the back cover those deleted.
 *
 *  @param[in] iIndex -- zero-based
 *  @param[in] nCount -- number of character to delete 
 *
 *  @return length of changed string
 */
int CFString::Delete(int iIndex, int nCount)
{ 
   pwstr_t pStart, pEnd;

   if (iIndex < 0 || iIndex >= m_nLen || nCount <= 0 || !m_szString) 
      return m_nLen;
      
   pStart = m_szString + iIndex;
   if (iIndex + nCount <= m_nLen)
      pEnd = pStart + nCount;
   else
      pEnd = m_szString + m_nLen;
   
   // assign characters from the back
   while (*pEnd != _S('\0')) {
      *pStart++ = *pEnd++;
   }
   *pStart = _S('\0');

   m_nLen = static_cast<int>(pStart - m_szString);

   return m_nLen;
}

/**
 *  Find a substring.
 *
 *  @param[in] pszSub -- the substring to find
 *  @param[in] iStart -- starting position to find
 *
 *  @return if found, zero-based index.
 *  @return if not found, -1.
 */
int CFString::Find(pcwstr_t pszSub, int iStart) const
{
   int len;
   pwstr_t pString, pEnd;

   if (!pszSub || !m_szString || iStart < 0 || iStart >= m_nLen) 
      return -1;

   len = static_cast<int>(CFString::WStrlen(pszSub));
   pString = m_szString + iStart;
   pEnd = m_szString + m_nLen;

   while (pString + len <= pEnd) {
      if (CFString::WStrncmp(pString, pszSub, len) == 0)
         return static_cast<int>(pString - m_szString);
      ++pString;
   }

   return -1;
}

/**
 *  Find a substring ignoring case.
 *
 *  @param[in] pszSub -- the substring to find
 *  @param[in] iStart -- starting position to find
 *
 *  @return if found, zero-based index.
 *  @return if not found, -1.
 */
int CFString::FindNoCase(pcwstr_t pszSub, int iStart) const
{
   int len;
   pwstr_t pString, pEnd;

   if (!pszSub || !m_szString || iStart < 0 || iStart >= m_nLen) 
      return -1;

   len = static_cast<int>(CFString::WStrlen(pszSub));
   pString = m_szString + iStart;
   pEnd = m_szString + m_nLen;

   while (pString + len <= pEnd) {
      if (CFString::WStrnicmp(pString, pszSub, len) == 0)
         return static_cast<int>(pString - m_szString);
      ++pString;
   }

   return -1;
}

/**
 *  Find a character.
 *
 *  @param[in] ch -- the character to find
 *  @param[in] iStart -- starting position to find
 *
 *  @return if found, zero-based index.
 *  @return if not found, -1.
 */
int CFString::Find(wc_t ch, int iStart) const
{
   wc_t szTemp[2] = {ch, _S('\0')};   

   return FindOneOf(szTemp, iStart);
}

/**
 *  Find one of the occurrences in the character set
 *
 *  @param[in] pszCharSet -- the character set
 *  @param[in] iStart -- starting index
 *  
 *  @return if found, zero-based index.
 *  @return if not found, -1.
 */
int CFString::FindOneOf(pcwstr_t pszCharSet, int iStart) const
{
    if (!pszCharSet || !m_szString || iStart < 0 || iStart >= m_nLen) 
      return -1;

    int nrSet = static_cast<int>(CFString::WStrlen(pszCharSet));
    int i;
    wc_t ch, *pString;

    pString = m_szString + iStart;
    while (pString < m_szString + m_nLen) {
       ch = *pString;
       for (i = 0; i < nrSet; ++i) {
          if (ch == pszCharSet[i])
             return static_cast<int>(pString - m_szString);
       }

       ++pString;
    }

    return -1;
}

/**
 *  Find a characher from the back
 *
 *  @param[in] ch -- the character to find
 *
 *  @return if found, zero-based index.
 *  @return if not found, -1.
 */
int CFString::ReverseFind(wc_t ch) const
{
   pwstr_t pString;

   if (!m_szString || m_nLen == 0) 
      return -1;

   pString = m_szString + m_nLen - 1;   
   
   while (pString >= m_szString) {
      if (*pString == ch)
        return static_cast<int>(pString - m_szString);
      --pString;
   }

   return -1;
}

/**
 *  Test if this string starts with a character
 */
bool CFString::StartsWith(wc_t ch) const
{
   wc_t szTemp[2] = {ch, _S('\0')};
   
   return StartsWith(szTemp);
}

/**
 *  Test if this string starts with a string
 */
bool CFString::StartsWith(pcwstr_t szString) const
{
   if (!m_szString || m_nLen == 0) 
      return false;

   sz_t len = CFString::WStrlen(szString);
   if (len == 0 || len > static_cast<sz_t>(this->m_nLen))
      return false;

   return (CFString::WStrncmp(this->m_szString, szString, len) == 0);
}

/**
 *  Test if this string ends with a character
 */
bool CFString::EndsWith(wc_t ch) const
{
   wc_t szTemp[2] = {ch, _S('\0')};
   
   return EndsWith(szTemp);
}

/**
 *  Test if this string ends with a string
 */
bool CFString::EndsWith(pcwstr_t szString) const
{
   if (!m_szString || m_nLen == 0) 
      return false;

   sz_t len = CFString::WStrlen(szString);
   if (len == 0 || len > static_cast<sz_t>(this->m_nLen))
      return true;

   pcwstr_t szTemp = this->m_szString + (this->m_nLen - len);
   
   return (CFString::WStrncmp(szTemp, szString, len) == 0);
}



/*********************************************************************
     Helper functions for Format(...) and AppendFormat(...)
 *********************************************************************/

//! return value of __GetFormatText
typedef enum _formattype
{
   FT_NONE = 0, ///< can't be recognized
   FT_HEX = 1,  ///< hex "%x"
   FT_SDEC,     ///< signed decimal "%d"
   FT_USDEC,    ///< unsigned decimal "%u"
   FT_64HEX,    ///< 64 bit hex "%llx"
   FT_64SDEC,   ///< signed 64 bit decimal "%lld"
   FT_64USDEC,  ///< unsigned 64 bit decimal "%llu"
   FT_STRING,   ///< UTF16 string "%s"
   FT_CHAR,     ///< UTF16 character "%c"
   FT_STRING_8, ///< UTF8 string "%S"
   FT_CHAR_8,   ///< UTF8 character "%C"
   FT_FLOAT_F,  ///< float (single or double precision, decimal float point) "%f" 
   FT_FLOAT_E,  ///< float (single or double precision, scientific notation) "%e" 
   FT_PERCENT   ///< percentage sign "%"
} formattype_t;

/**
 *   @brief To print a plain string. 
 *
 *   (Helper function for "Format" and "AppendFormat")
 *
 *   @param[in,out] pszString -- the string pointer to traverse our internal
 *                                string buffer 
 *   @param[in] isParseOnly -- if true, update position of pszString only
 *   @param[in] szStart -- inclusive
 *   @param[in] szEnd -- exclusive, NULL if it is null-terminated
 */
PRIVATE void __PrintString(wc_t ** pszString, bool isParseOnly, pcwstr_t szStart, pcwstr_t szEnd)
{        
    sz_t stringLen = (szEnd) ? (szEnd - szStart) : CFString::WStrlen(szStart);
    if (stringLen > 0 && !isParseOnly)
       CFString::Memcpy(*pszString, szStart, stringLen * sizeof(wc_t));

    // update in whatever condition
    *pszString += stringLen;
}

/**
 *  Print UTF-8 string
 */
PRIVATE void __PrintString_8(wc_t ** pszString, bool isParseOnly, const char * szStart, const char * szEnd)
{        
    sz_t stringLen = (szEnd) ? (szEnd - szStart) : CFString::Strlen(szStart);
    int utf16Len = text::UTF8ToUTF16(szStart, stringLen, NULL, 0);

    if (utf16Len > 0 && !isParseOnly) {
       const int LOCALBUFSIZE = 96;  // just an arbitrary number

       if (utf16Len <= LOCALBUFSIZE) {   
          wc_t utf16Buf[LOCALBUFSIZE];   // use local buffer if it is a short string
          text::UTF8ToUTF16(szStart, stringLen, utf16Buf, utf16Len);
          CFString::Memcpy(*pszString, utf16Buf, utf16Len * sizeof(wc_t));
       }
       else {
          wc_t *pUtf16Buf = new (cppmempool::NEWPTR) wc_t[utf16Len];
          text::UTF8ToUTF16(szStart, stringLen, pUtf16Buf, utf16Len);
          CFString::Memcpy(*pszString, pUtf16Buf, utf16Len * sizeof(wc_t));
          cppmempool::Release(pUtf16Buf);
       }
    }

    // update in whatever condition
    *pszString += utf16Len;
}

/**
 *  @brief String -> decimal number. 
 *  
 *  Helper function for "Format" and "AppendFormat"
 *
 *  @param[in] szString -- string that contains a decimal number
 *                         (may not be NULL terminated and will stop
 *                          once a non-decimal character is encountered).
 *
 *  @return the integer parsed
 *
 */
PRIVATE int __String2Decimal(pcwstr_t szString)
{
   int num = 0;
   wc_t ccChar;

   do {
      ccChar = *szString++;
      if (ccChar < _S('0') || ccChar > _S('9'))
         break;

      num = num * 10 + (ccChar - _S('0'));
   } while (true);

   return num;	

}

/**
 * Buffer size to hold an integer in decimal.
 * Integer (64 bit) will have a maximum of 20 digits + 1 sign.
 */ 
#define NUM2STR_BUFSIZE	21

/**
 * macros to construct the number of spaces or zeros to be prefixed
 */
#define MAKENRSPACE(nrSpace) nrSpace = (0x1 << 16) | nrSpace;
#define MAKENRZERO(nrZero) nrZero = (0x2 << 16) | nrZero;
#define IS_SPACE(nrSpaceZero) (nrSpaceZero & 0x10000)
#define GETNUMSPZERO(nrSpaceZero) (nrSpaceZero & 0xffff)

/**
 *  @brief To fill leading space/zero of a number. 
 *
 *  Helper function for "Format" and "AppendFormat"
 *
 *  @param[in,out] pszString -- the string pointer to traverse our internal
 *                               string buffer
 *  @param[in] isParseOnly -- if true, update position of pszString only
 *  @param[in] nrSpaceZero -- number of space or zero ahead
 *                             (low 16-bit is the number and high bits
 *                              indicate "space" or "zero".)
 *  @param[in] nrDeduction -- number of digits to be deducted by nrSpaceZero
 *
 */
PRIVATE void __FillSpaceZero(wc_t ** pszString, bool isParseOnly, int nrSpaceZero, int nrDeduction)
{
   int i;
   wc_t ccChar;

   ccChar = IS_SPACE(nrSpaceZero) ? _S(' ') : _S('0');
   nrSpaceZero = GETNUMSPZERO(nrSpaceZero);
   // we don't count those chars that have been written
   nrSpaceZero -= nrDeduction;

   // only meaningful if this is greater than 0
   if (nrSpaceZero > 0) {
      wc_t * pChar = *pszString;

      if (!isParseOnly) {         
         for (i = 0 ; i < nrSpaceZero; ++i) 
            *pChar++ = ccChar;         
      }      

      // update anyway
      *pszString += nrSpaceZero;
   }
}

/**
 *  @brief Decimal number (signed or unsigned) -> string. 
 *
 *  Helper function for "Format" and "AppendFormat".
 *
 *  @note We fill the buffer (that holds the integer) from tail to head.
 *
 *
 *  @param[in,out] pszString -- the string pointer to traverse our internal
 *                              string buffer
 *  @param[in] isParseOnly -- if true, update position of pszString only
 *  @param[in] nrSpaceZero -- number of space or zero ahead
 *  @param[in] numToConvert -- the number to be converted to string
 *  @param[in] isSigned -- whether it should be printed as a signed number
 * 
 */

PRIVATE void __PrintDecimal(wc_t ** pszString, bool isParseOnly, int nrSpaceZero, 
                            i64_t numToConvert, bool isSigned)
{
   bool isNeg = (numToConvert < 0);
   u64_t numCopy;
   wc_t numBuf[NUM2STR_BUFSIZE];
   wc_t * pBufEnd = numBuf + NUM2STR_BUFSIZE - 1; // point to last valid char
   int nrDeduction;

   // make it positive first -- 2's complement
   if (isSigned && isNeg) {
      numCopy = ~numToConvert;
      numCopy += 1;
   }
   else {
      numCopy = numToConvert;
   }

   
   // assign number from tail
   if (numCopy == 0) {
      *pBufEnd-- = _S('0');
   }
   else {
      int remainder;
      while (numCopy) {
         remainder = static_cast<int>(numCopy % 10);
         *pBufEnd-- = _S('0') + remainder;
         numCopy /= 10;
      }
   }

   // prefix it with a neg sign '-'
   if (isSigned && isNeg) {
      *pBufEnd-- = _S('-');
   }

   //  move back one character
   //+ and left the number to be written later
   pBufEnd++;
 
   // number of char written in numBuf
   nrDeduction = static_cast<int>((numBuf + NUM2STR_BUFSIZE) - pBufEnd);
   
   // write space or zero, first   
   __FillSpaceZero(pszString, isParseOnly, nrSpaceZero, nrDeduction);
   
   // write number at the end      
   if (!isParseOnly) 
      CFString::Memcpy(*pszString, pBufEnd, nrDeduction * sizeof(wc_t));

   // update
   *pszString += nrDeduction;

}

/**
 *  @brief hex number -> string. 
 *
 *  Helper function for "Format" and "AppendFormat".
 *
 *  @param[in,out] pszString -- the string pointer to traverse our internal
 *                              string buffer
 *  @param[in] isParseOnly -- if true, update position of pszString only
 *  @param[in] nrSpaceZero -- number of space or zero ahead
 *  @param[in] numToConvert -- the number to be converted to string
 */
PRIVATE void __PrintHex(wc_t ** pszString, bool isParseOnly, int nrSpaceZero, i64_t numToConvert)
{
   wc_t numBuf[NUM2STR_BUFSIZE];
   wc_t * pBufEnd = numBuf + NUM2STR_BUFSIZE - 1; // point to last valid char   
   int nrDeduction;   
   
   // assign number from tail
   if (numToConvert == 0) {
      *pBufEnd-- = _S('0');
   }
   else {      
      int nibble;
      while (numToConvert) {
         nibble = static_cast<int>(numToConvert & static_cast<i64_t>(0x0f));
         if (nibble >= 10)
           *pBufEnd-- = _S('a') + (nibble - 10);
         else 
           *pBufEnd-- = _S('0') + nibble;

         numToConvert = numToConvert >> 4;
         //  to prevent negative number from shifting 1s
         //+ by the above operation, set the highest 4 bits
         //+ to zero.
         numToConvert &= ~(static_cast<i64_t>(0xf) << (sizeof(i64_t) * 8 - 4));
      }
   }

   //  move back one character
   //+ and left the number to be written later
   pBufEnd++;

   // number of char written in numBuf
   nrDeduction = static_cast<int>((numBuf + NUM2STR_BUFSIZE) - pBufEnd);
   
   // write space or zero, first
   __FillSpaceZero(pszString, isParseOnly, nrSpaceZero, nrDeduction);
      
   // write number at the end      
   if (!isParseOnly) 
      CFString::Memcpy(*pszString, pBufEnd, nrDeduction * sizeof(wc_t));

   // update
   *pszString += nrDeduction;

}

/**
 *  Limitation of the number of leading space/zero
 */
#define LIMIT_SPACEZERO	128

/**
 *   @brief To print the number in various format (hex, decimal, signed decimal).
 *
 *   Helper function for "Format" and "AppendFormat".
 *
 *   @param[in,out] pszString -- the string pointer to traverse our internal
 *                               string buffer
 *   @param[in] isParseOnly -- if true, update position of pszString only
 *   @param[in] szFormat -- the format text extracted in "GetFormatText"
 *   @param[in] formatType -- type of data (ie FT_***)
 *   @param[in] num -- the number to be printed
 *
 */
PRIVATE void __PrintNum(wc_t ** pszString, bool isParseOnly, pcwstr_t szFormat, 
                        formattype_t formatType, i64_t num)
{    
    // the first one (0 position) is the percentage sign '%'
    int nrSpaceZero = __String2Decimal(&szFormat[1]);

    // limit the number of space/zero
    if (nrSpaceZero > LIMIT_SPACEZERO)
       nrSpaceZero = LIMIT_SPACEZERO;
   
    // see if we have to prefix spaces or zeros
    if (szFormat[1] == _S('0')) {
       MAKENRZERO(nrSpaceZero);
    }
    else if (szFormat[1] >= _S('1') && szFormat[1] <= _S('9')) {
       MAKENRSPACE(nrSpaceZero);
    }
       
    // call different conversion functions
    switch (formatType) {
       case FT_HEX:
       case FT_64HEX:
          __PrintHex(pszString, isParseOnly, nrSpaceZero, num);
          break;
       case FT_SDEC:
       case FT_64SDEC:
          __PrintDecimal(pszString, isParseOnly, nrSpaceZero, num, true);
          break;
       case FT_USDEC:
       case FT_64USDEC:
          __PrintDecimal(pszString, isParseOnly, nrSpaceZero, num, false);
          break;
    }

    //
    // so much work just to replace the "sprintf" evil !
    // 
    //_stprintf_s(buf, TEMPBUFSIZE, szFormat, num); 
    //
    
}

/**
 *   @brief To print the floating point number in decimal or scientific notation format
 *
 *   Helper function for "Format" and "AppendFormat".
 *
 *   @param[in,out] pszString -- the string pointer to traverse our internal
 *                               string buffer
 *   @param[in] isParseOnly -- if true, update position of pszString only
 *   @param[in] szFormat -- the format text extracted in "GetFormatText"
 *   @param[in] formatType -- type of data (ie FT_FLOAT_F or FT_FLOAT_E)
 *   @param[in] fNum -- floating point number to be printed
 */
PRIVATE void __PrintFloat(wc_t ** pszString, bool isParseOnly, pcwstr_t szFormat, 
                          formattype_t formatType, double fNum)
{

// the resultant floating point string length
#define FSTRBUFSIZE 64 

#if defined(WIN32)

   wc_t floatStrBuf[FSTRBUFSIZE];
   int nrChar = _snwprintf(floatStrBuf, FSTRBUFSIZE, szFormat, fNum);
   if (nrChar < 0) { // buffer not big enough
      floatStrBuf[FSTRBUFSIZE - 1] = _S('\0');
   }
   else if (nrChar >= 0) {
      if (nrChar >= FSTRBUFSIZE) nrChar = FSTRBUFSIZE - 1;
      floatStrBuf[nrChar] = _S('\0');
   }

   __PrintString(pszString, isParseOnly, floatStrBuf, NULL);

#elif defined(linux)

// szFormat is no longer than TEMPBUFSIZE
#define FORMATBUFSIZE 32 

   char formatBuf[FORMATBUFSIZE];
   char floatStrBuf[FSTRBUFSIZE];

   int nrByte = cppflib::text::UTF16ToUTF8(szFormat, CFString::WStrlen(szFormat), formatBuf, FORMATBUFSIZE - 1);
   formatBuf[nrByte] = '\0';
   // snprintf won't overflow
   int nrChar = snprintf(floatStrBuf, FSTRBUFSIZE, formatBuf, fNum);
   if (nrChar >= FSTRBUFSIZE) { // buffer is not big enough
      nrChar = FSTRBUFSIZE - 1;
   }
   else if (nrChar < 0) {  // error occurs

#define QMARKLEN 5

      for (int i = 0; i < QMARKLEN; ++i)
         floatStrBuf[i] = '?';    // fill with question mark
      nrChar = QMARKLEN;
   }

   if (!isParseOnly) {
      wc_t *pTemp = *pszString;
      for (int i = 0; i < nrChar; ++i)
         *pTemp++ = static_cast<wc_t>(floatStrBuf[i]); // safe to do so since all are ASCII 
   }
   
   *pszString += nrChar;
   

#endif

}


/**
 *  @brief To get (extract actually) the format text (eg: %09d) within the format 
 *         string passed in. 
 *
 *  Helper function for "Format" and "AppendFormat"
 *
 *  @param[in,out] pTextStart -- start of the format text (it must be started
 *                                with '%'). On return, it will contain the 
 *                                advanced buffer pointer.
 *  @param[out] buf -- the buffer to hold the extracted format text
 *  @param[in] bufSizeInChar -- size of buffer in character
 *
 *  @return  0  -- no format found
 *  @return > 0 -- one of the format (e.g. FT_STRING, FT_CHAR)
 */
PRIVATE formattype_t __GetFormatText(wc_t ** pTextStart, wc_t * buf, int bufSizeInChar)
{

#define NR64LONGCHAR 2

    wc_t tmpChar;
    wc_t *pText = *pTextStart;
    wc_t *pBufStart = buf;
    wc_t *pBufEnd = pBufStart + bufSizeInChar;
    int nr64Long = 0; // to count number of 'l' (64 bit type)
    formattype_t retType = FT_NONE;
    
    // the 1st char must be '%'
    while (true) {
       if (buf < pBufEnd - 1)
          *buf++ = *pText;
       

       tmpChar = *(++pText);
       if (tmpChar == _S('\0')) {
          retType = FT_NONE;
          break;
       }
       else if (tmpChar == _S('d')) {
          retType = (nr64Long == NR64LONGCHAR) ? FT_64SDEC : FT_SDEC;
          break;
       }
       else if (tmpChar == _S('u')) {
          retType = (nr64Long == NR64LONGCHAR) ? FT_64USDEC : FT_USDEC;
          break;
       }
       else if (tmpChar == _S('x')) {
          retType = (nr64Long == NR64LONGCHAR) ? FT_64HEX : FT_HEX;
          break;
       }
       else if (tmpChar == _S('s')) {
          retType = (nr64Long == 0) ? FT_STRING : FT_NONE;
          break;
       }
       else if (tmpChar == _S('c')) {
          retType = (nr64Long == 0) ? FT_CHAR : FT_NONE;
          break;
       }
       else if (tmpChar == _S('S')) {
          retType = (nr64Long == 0) ? FT_STRING_8 : FT_NONE;
          break;
       }
       else if (tmpChar == _S('C')) {
          retType = (nr64Long == 0) ? FT_CHAR_8 : FT_NONE;
          break;
       }
       else if (tmpChar == _S('f')) {
          retType = (nr64Long == 0) ? FT_FLOAT_F : FT_NONE;
          break;
       }
       else if (tmpChar == _S('e')) {
          retType = (nr64Long == 0) ? FT_FLOAT_E : FT_NONE;
          break;
       }
       else if (tmpChar == _S('p')) {  // print pointer in hex number
          if (nr64Long != 0) {
             retType = FT_NONE;
             break;
          }

          buf = pBufStart + 1;
          if (sizeof(ptr_t) == sizeof(u32_t)) {      // 32 bit pointer (force it to be %08x)
             *buf++ = _S('0'); *buf++ = _S('8');
             retType = FT_HEX;
          }
          else if (sizeof(ptr_t) == sizeof(u64_t)) { // 64 bit pointer (force it to be %016x)
             *buf++ = _S('0'); *buf++ = _S('1'); *buf++ = _S('6');
             retType = FT_64HEX;
          }
          tmpChar = _S('x');     
          break;
       }
       else if (tmpChar == _S('%')) {
          retType = FT_PERCENT;
          break;
       }
       else if (tmpChar == _S('l')) {
          ++nr64Long;
       }
       else {
          if (tmpChar != _S('.') && (tmpChar < _S('0') || tmpChar > _S('9')))
             break;

          if (nr64Long != 0)
             break;
       }

    } // end of while

    
    //
    // update pointer on return
    if (tmpChar == _S('\0')) {
       *pTextStart = pText;
    }
    else {
       *pTextStart = pText + 1;

       // 
       // assign residue    
       if (buf < pBufEnd - 1)
          *buf++ = tmpChar;  
       else 
          retType = FT_NONE; // buffer full, make it an unknown type     
           
    }

    *buf = _S('\0');
    
    return retType;
}

/** 
 *   @brief "sprintf"-like function but cannot handle floating numbers (float and double).
 *
 *   We will parse the format string twice :
 *      -- 1st parse : calculate the number of characters needed
 *      -- 2nd parse : write the string
 *
 *   @param[in] pszFormat -- the formatted text
 *   @param[in] ... -- other parameters
 */
void CFString::Format(pcwstr_t pszFormat, ...)
{
    __InternalFormat(&pszFormat, 0);
}

/**
 *   @brief "sprintf"-like function but will append rather than replace
 *
 *   Most are just the same as Format() but the starting
 *   string pointer is set to the end of string.
 *
 *   @param[in] pszFormat -- the formatted text
 *   @param[in] ... -- other parameters
 */
void CFString::AppendFormat(pcwstr_t pszFormat, ...)
{
    __InternalFormat(&pszFormat, m_nLen);
}

#if defined(linux)
#   if !defined(_INTSIZEOF)
#      define _INTSIZEOF(n) ( (sizeof(n) + sizeof(ptr_t) - 1) & ~(sizeof(ptr_t) - 1) )
#   endif
#endif

/**
 *  Cannot explicitly cast an address value to 'va_list' on Android and on 
 *  newer version of g++ (>= 4.7.0) thus,
 *  an ugly hack is performed here: direct copy the address to 'ap'
 */
#if defined(__GNUC__)
#   define __arg_start(ap,v)\
          do {\
             ptr_t temp = reinterpret_cast<ptr_t>(v) + _INTSIZEOF(v); \
             CFString::Memcpy(&ap, &temp, sizeof(ptr_t)); \
          } while(false)
#else
#   define __arg_start(ap,v) ( ap = (va_list)(v) + _INTSIZEOF(v) )
#endif

//! buffer size to hold the format specifier
#define TEMPBUFSIZE 21

/**
 *  This one is not intended to be used directly. Use 'Format', 'AppendFormat' instead
 *
 *  @param [in] addrOfFormat -- address of format string in 'Format' or 'AppendFormat'
 *  @param [in] strStartWriteIndex -- starting index of the internal buffer for writing
 */
void CFString::__InternalFormat(pcwstr_t *addrOfFormat, int strStartWriteIndex)
{
    if (!addrOfFormat)
       return;

    if (strStartWriteIndex < 0 || strStartWriteIndex > m_nLen)
       return;

    wc_t * pText;
    wc_t * pTextStart, * pTextEnd;
    wc_t * pszString;
    wc_t buf[TEMPBUFSIZE];
    bool isParseOnly = true;
    formattype_t formatType;
    pcwstr_t pszFormat = *addrOfFormat;
    va_list marker;

    if (!pszFormat)
       return;

    // loop 2 times: 
    //    1) Parse
    //    2) Actual writing
    for (int loop = 0; loop < 2; ++loop) {

       // prepare
       __arg_start(marker, addrOfFormat);
       //marker = (va_list)addrOfFormat + _INTSIZEOF(pszFormat);
       pTextStart = pText = (wc_t*)pszFormat;
       pszString = m_szString + strStartWriteIndex;

       while (*pText != _S('\0')) {       
       
          if (*pText == _S('%')) {

             pTextEnd = pText;

             // print any string prior to this format specifier
             __PrintString(&pszString, isParseOnly, pTextStart, pTextEnd);

             formatType = __GetFormatText(&pText, buf, TEMPBUFSIZE);
             switch (formatType) {
                case FT_SDEC:
                   __PrintNum(&pszString, isParseOnly, buf, formatType, static_cast<i64_t>(va_arg(marker, int)));
                   break;
                case FT_USDEC:
                case FT_HEX:
                   __PrintNum(&pszString, isParseOnly, buf, formatType, static_cast<i64_t>(va_arg(marker, int)) & I64N(0xffffffff));
                   break;
                case FT_64SDEC:
                case FT_64USDEC:
                case FT_64HEX:
                   __PrintNum(&pszString, isParseOnly, buf, formatType, va_arg(marker, i64_t));
                   break;
                case FT_FLOAT_F:
                case FT_FLOAT_E:
                   // no matter data is of 'float' or 'double', it is always promoted to 'double'
                   __PrintFloat(&pszString, isParseOnly, buf, formatType, va_arg(marker, double));
                   break;
                case FT_STRING:
                   __PrintString(&pszString, isParseOnly, va_arg(marker, wc_t*), NULL);
                   break;
                case FT_CHAR:
                   //  though a character is expected, the size pushed on
                   //+ stack is still the size of integer
                   buf[0] = static_cast<wc_t>(va_arg(marker, int) & 0xffff);
                   buf[1] = _S('\0');
                   __PrintString(&pszString, isParseOnly, buf, NULL);
                   break;
                case FT_STRING_8:
                   __PrintString_8(&pszString, isParseOnly, va_arg(marker, const char*), NULL);
                   break;
                case FT_CHAR_8:
                   //  though a character is expected, the size pushed on
                   //+ stack is still the size of integer
                   buf[0] = static_cast<wc_t>(va_arg(marker, int) & 0xff);
                   buf[1] = _S('\0');
                   __PrintString(&pszString, isParseOnly, buf, NULL);
                   break;
                case FT_PERCENT:
                   buf[0] = _S('%');
                   buf[1] = _S('\0');
                   __PrintString(&pszString, isParseOnly, buf, NULL);
                   break;
                case FT_NONE:
                   __PrintString(&pszString, isParseOnly, buf, NULL);
                   //  skip one parameter and assume it to be
                   //+ the size of an integer
                   va_arg(marker, int);
                   break;
             }
          
             // update
             pTextStart = pText;
          }
          else {
             ++pText;
          }

       } // end of while

       pTextEnd = pText;
       // print any residue string 
       __PrintString(&pszString, isParseOnly, pTextStart, pTextEnd);
    
       va_end(marker);


       //
       // modify state
       if (isParseOnly) {
          // calculate size required 
          int sizeRequire = static_cast<int>(pszString - m_szString);

          // nothing to write, break the loop
          if (sizeRequire == strStartWriteIndex)
             break;

          // include terminating NULL
          if (sizeRequire + 1 > m_nSize)
             ReAllocBuffer(sizeRequire + 1);
          
          // switch to writing phase
          isParseOnly = false;
       }
       else {
          // mark end of string
          *pszString = _S('\0');
          // update length
          m_nLen = static_cast<int>(pszString - m_szString);
       }

    }
}


/*********************************************************************
     End of helper functions for Format(...) and AppendFormat(...)
 *********************************************************************/

/**
 *  Append string to this string
 *
 *  @param [in] psz -- string to be appended
 *  @param [in] nLen -- length of psz to be appended (can be less than length of psz)
 */
void CFString::Append(pcwstr_t psz, int nLen)
{
   if (!psz || nLen <= 0)
      return;

   int orgLen = static_cast<int>(CFString::WStrlen(psz));
   nLen = cppflib::Min<int>(nLen, orgLen);

   InitUTF16ToWideString(psz, nLen, 0, true);
}

/**
 *  @brief internal use only
 *
 *  Insert string with buffer size specified.
 *
 *  @param[in] nIndex -- zero-based index
 *  @param[in] psz -- string to be inserted
 *  @param[in] nSize -- size of buffer wished to be allocated,
 *                      pass 0 if not cared.
 *
 *  @return length of changed string
 */
int CFString::Insert(int nIndex, pcwstr_t psz, int nSize)
{
   pwstr_t pStrStart, pStrEnd;
   int len, nSizeRequest;

   if (!psz)
      return m_nLen;

   if ((len = static_cast<int>(CFString::WStrlen(psz))) == 0)
      return m_nLen;

   // adjust parameter
   if (nIndex < 0) nIndex = 0;
   else if (nIndex > m_nLen) nIndex = m_nLen;

   // reallocate if buffer isn't big enough
   nSizeRequest = cppflib::Max<int>(nSize, m_nLen + len + 1);
   if (nSizeRequest > m_nSize) 
      ReAllocBuffer(nSizeRequest);

   // first move the latter part to the back
   pStrStart = m_szString + m_nLen;
   pStrEnd = pStrStart + len;
   while (pStrStart >= m_szString + nIndex) {
      *pStrEnd-- = *pStrStart--;
   }

   // move back one character
   ++pStrStart;

   CFString::Memcpy(pStrStart, psz, len * sizeof(wc_t));
   
   // update string length
   m_nLen += len;   

   return m_nLen;
}

/**
 *  @brief internal use only
 *
 *  Insert string with buffer size specified.
 *  Most code are just the same as the UNICODE string counterpart.
 *
 *  @param[in] nIndex  -- zero-based index
 *  @param[in] psz  -- string to be inserted
 *  @param[in] nSize  -- size of buffer wished to be allocated,
 *                       pass 0 if not cared.
 *
 *  @return length of changed string
 */
int CFString::Insert(int nIndex, const char * psz, int nSize)
{
   pwstr_t pStrStart, pStrEnd;
   int len, wideLen, nSizeRequest;

   if (!psz)
      return m_nLen;

   if ((len = static_cast<int>(CFString::Strlen(psz))) == 0)
      return m_nLen;

   // compute the length for conversion
   wideLen = cppflib::text::UTF8ToUTF16(psz, len, NULL, 0);

   // adjust parameter
   if (nIndex < 0) nIndex = 0;
   else if (nIndex > m_nLen) nIndex = m_nLen;

   // reallocate if buffer isn't big enough
   nSizeRequest = cppflib::Max<int>(nSize, m_nLen + wideLen + 1);
   if (nSizeRequest > m_nSize) 
      ReAllocBuffer(nSizeRequest);

   // first move the latter part to the back
   pStrStart = m_szString + m_nLen;
   pStrEnd = pStrStart + wideLen;
   while (pStrStart >= m_szString + nIndex) {
      *pStrEnd-- = *pStrStart--;
   }

   // move back one character
   ++pStrStart;

   // actual conversion here
   cppflib::text::UTF8ToUTF16(psz, len, pStrStart, wideLen);
   
   // update string length
   m_nLen += wideLen;   

   return m_nLen;

}

/**
 *  @brief internal use only
 *
 *  Insert and write length and size at the end
 */
int CFString::InsertWriteLenAndSize(int nIndex, pcwstr_t psz, int nSize)
{
   int ret = Insert(nIndex, psz, nSize);
   __WriteStrLenAndSize(this);
   return ret;
}

/**
 *  @brief internal use only
 *
 *  Insert and write length and size at the end
 */
int CFString::InsertWriteLenAndSize(int nIndex, const char * psz, int nSize)
{
   int ret = Insert(nIndex, psz, nSize);
   __WriteStrLenAndSize(this);
   return ret;
}

/**
 *  Insert a string at specified position.
 *
 *  @param[in] nIndex -- zero-based index
 *  @param[in] psz -- string to be inserted
 *
 *  @return length of changed string
 */
int CFString::Insert(int nIndex, pcwstr_t psz)
{   
   return Insert(nIndex, psz, 0);
}

/**
 *  Insert a character at specified position.
 *
 *  @param[in] nIndex -- zero-based index
 *  @param[in] ch -- character to be inserted
 *
 *  @return length of changed string
 */
int CFString::Insert(int nIndex, wc_t ch)
{
   wc_t temp[2] = {ch, _S('\0')};

   return Insert(nIndex, temp, 0);
}

/**
 *  Insert a UTF-8 string at specified position.
 *
 *  @param[in] nIndex -- zero-based index
 *  @param[in] psz -- string to be inserted
 *
 *  @return length of changed string
 */
int CFString::Insert(int nIndex, const char * psz)
{
   return Insert(nIndex, psz, 0);
}

/**
 *  Removes all instances of the specified character from the string
 *
 *  @param[in] chRemove -- character to be removed
 *
 *  @return the count of characters removed from the string
 */
int CFString::Remove(wc_t chRemove)
{
   if (!m_szString || m_nLen == 0)
      return 0;

   int nCount = 0;
   pwstr_t pStrStart, pStrEnd;
   wc_t tc;
    
   pStrStart = pStrEnd = m_szString;
   tc = *pStrEnd;
   while (tc != _S('\0')) {
      
      //
      //  Find the character;
      //+ If found, don't increment "pStrStart" but only "pStrEnd".
      //
      if (tc != chRemove) {
         if (pStrEnd > pStrStart) {
            *pStrStart = tc;
         }

         // increment whatever
         ++pStrStart;
      }
      else {
         ++nCount;
      }
      
      tc = *(++pStrEnd);
   }

   // mark the end if 'pStrStart' is less than 'pStrEnd'
   if (pStrStart < pStrEnd) {
      *pStrStart = _S('\0');
      // update length, too
      m_nLen = static_cast<int>(pStrStart - m_szString);
   }      

   return nCount;
}

/**
 *  Replace the old character with the new character.
 *
 *  @param[in] chOld -- character to be replaced
 *  @param[in] chNew -- the new character to replace
 *
 *  @return the number of replaced characters
 */
int CFString::Replace(wc_t chOld, wc_t chNew)
{
   wc_t pszOld[2] = {chOld, _S('\0')};
   wc_t pszNew[2] = {chNew, _S('\0')};

   return Replace(pszOld, pszNew);
}

/**
 *  Replace the old substring with the new substring.
 *
 *  @param[in] pszOld -- old substring
 *  @param[in] pszNew -- new substring (can be NULL)
 *
 *
 *  @note lenght of old and new string may not be the same,
 *        so the resultant string may grow or shrink.
 *
 *  @return the number of replaced substring
 */
int CFString::Replace(pcwstr_t pszOld, pcwstr_t pszNew)
{   
   int oldLen, newLen;
   int nStart = 0, nCount = 0;

   if (!pszOld)
      return 0;

   if ((oldLen = static_cast<int>(CFString::WStrlen(pszOld))) == 0)
      return 0;
  
   if (!m_szString || m_nLen == 0)
      return 0;

   
   newLen = static_cast<int>(CFString::WStrlen(pszNew));
   

   while (nStart < m_nLen) {

      if (CFString::WStrncmp(&m_szString[nStart], pszOld, oldLen) == 0) {

         if (oldLen == newLen) {
            // we copy directly here
            CFString::Memcpy(&m_szString[nStart], pszNew, newLen * sizeof(wc_t));
         }
         else if (newLen < oldLen) {
            // this will reserve a hole
            Delete(nStart + newLen, oldLen - newLen);
            // copy directly
            if (pszNew && newLen > 0)
               CFString::Memcpy(&m_szString[nStart], pszNew, newLen * sizeof(wc_t));
         }
         else {
            Delete(nStart, oldLen);
            Insert(nStart, pszNew);
         }

         ++nCount;

         nStart += newLen;
      }
      else {
         ++nStart;
      }

   } // end of while

   return nCount;
}

/**
 *  @brief Internal use only
 *
 *  To get a substring (a new copy). Used by "Left", "Right", and "Mid".
 *
 *  @param[in] nStart -- zero-based starting position
 *  @param[in] nCount -- number of character to extract
 *  @param[in] isReverse -- true to get from the right
 *
 *  @note If "isReverse" is true, nStart is ignored.
 *
 *  @return pointer to the new string extracted.
 */
pwstr_t CFString::GetSubString(int nStart, int nCount, bool isReverse) const
{
   int nCharLeft;
   pwstr_t pString;
   CFString szTemp;

   // adjust start
   if (nStart < 0) nStart = 0;
   else if (nStart > m_nLen) nStart = m_nLen;

   // adjust count 
   nCharLeft = m_nLen - nStart;   // we have only this number of character left to get
   if (nCount < 0) nCount = 0;
   else if (nCount > nCharLeft) nCount = nCharLeft;

   // adjust string start 
   pString = this->m_szString;
   if (isReverse) {
      // for "Right"
      pString += (m_nLen - nCount);
   }
   else {
      // for "Mid"
      pString += nStart;
   }

   // reserve space to write length and size
   szTemp.InitUTF16ToWideString(pString, nCount, __GetStrLenReserved(nCount));
   
   // append length and size at the end
   __WriteStrLenAndSize(&szTemp);

   return szTemp.Detach();
}

/**
 *  To get a substring from the left
 * 
 *  @param[in] nCount -- number of characters to get
 *
 *  @return a new copy of CFString
 */
CFString CFString::Left(int nCount) const
{
   // this will call our private constructor
   return CFString(static_cast<const void*>(GetSubString(0, nCount)));
}

/**
 *  To get a substring from the right
 * 
 *  @param[in] nCount -- number of characters to get
 *
 *  @return a new copy of CFString
 */
CFString CFString::Right(int nCount) const
{
   // this will call our private constructor
   return CFString(static_cast<const void*>(GetSubString(0, nCount, true)));
}

/**
 *  To get a substring in the middle
 * 
 *  @param[in] iFirst -- starting position to get
 *  @param[in] nCount -- number of characters to get
 *
 *  @return a new copy of CFString
 */
CFString CFString::Mid(int iFirst, int nCount) const
{
   // default value
   if (nCount < 0) nCount = m_nLen;

   // this will call our private constructor
   return CFString(static_cast<const void*>(GetSubString(iFirst, nCount)));
}

#ifdef WIN32
/**
 *  @brief Load a string from resource
 *
 *  Finding a string is somewhat different than other resources.
 *  We have to change the string ID to a bundle group ID (each group
 *  consists of 16 members).
 *  On obtaining the group, we have to walk through the group to
 *  get our own string.
 *
 *  @param[in] hInstance -- instance of module that contains the string
 *  @param[in] nID -- resource ID of the string
 *
 *  @return true for OK
 *  @return false for failed
 */
bool CFString::LoadString(ptrany_t hInstance, u32_t nID)
{  
   //! Number of string in a string resource "bundle"
   const int NR_STR_IN_BUNDLE = 16;

   HRSRC hrSrc;
   HGLOBAL hStr;
   UINT strLen;
   wc_t * pwsz;
   int i;

   // first get the bundle containing our string
   hrSrc = ::FindResource(reinterpret_cast<HINSTANCE>(hInstance), MAKEINTRESOURCE(nID / NR_STR_IN_BUNDLE + 1), RT_STRING);
   if (!hrSrc)
      return false;
        
   hStr = LoadResource(reinterpret_cast<HINSTANCE>(hInstance), hrSrc);
   pwsz = static_cast<wc_t*>(LockResource(hStr));
   
   //
   //  we now have to walk through the bundle
   //+ The first word (2 bytes) before each string
   //+ contains the length of the string.
   nID = nID % NR_STR_IN_BUNDLE;
   for (i = 0; i < static_cast<int>(nID); ++i) {
      pwsz += (1 + static_cast<int>(*pwsz));
   }

   strLen = static_cast<int>(*pwsz);
   InitUTF16ToWideString(pwsz + 1, strLen, strLen + 1, false);

   // it is useless now
   FreeResource(hStr);

   return true;
}

/**
 *  @brief Load a string from local resource
 *
 *  @param[in] nID -- string resource ID
 *
 *  @return true for OK
 *  @return false for failed
 */
bool CFString::LoadString(u32_t nID)
{
   return LoadString(::GetModuleHandle(NULL), nID);
}

#endif

/**
 *  @brief Internal use only
 *
 *  To change the case of a string
 *
 *  @param[in] upper -- unicode number of upper limit to be changed
 *  @param[in] lower -- unicode number of lower limit to be changed
 *  @param[in] disp -- displacement to be applied to character
 */
void CFString::ChangeCase(int upper, int lower, int disp)
{     
   if (!m_szString || m_nLen == 0)
      return;

   pwstr_t pString = m_szString;
   int ch;
      
   while (pString < m_szString + m_nLen) {
      ch = static_cast<int>(*pString);
      if (ch >= lower && ch <= upper)
         *pString += disp;

      ++pString;
   }      

}

/**
 *  Convert characters to lower case
 *
 *  @return reference to this string
 */
CFString& CFString::MakeLower()
{
   ChangeCase(static_cast<int>(_S('Z')), static_cast<int>(_S('A')), static_cast<int>((_S('a') - _S('A'))));
   return *this;
}

/**
 *  Convert characters to upper case
 *
 *  @return reference to this string
 */
CFString& CFString::MakeUpper()
{
   ChangeCase(static_cast<int>(_S('z')), static_cast<int>(_S('a')), static_cast<int>((_S('A') - _S('a'))));
   return *this;
}

/**
 *  Reverse the order of the characters in this string
 *
 *  @return reference to this string
 */
CFString& CFString::MakeReverse()
{
   do {
      if (!m_szString || m_nLen <= 1)
         break;

      pwstr_t pStart, pEnd;
      wc_t ch;

      pStart = m_szString;
      pEnd = m_szString + m_nLen - 1;

      while (pStart < pEnd) {
         // swap the characters
         ch = *pStart;
         *pStart = *pEnd;
         *pEnd = ch;

         ++pStart;
         --pEnd;
      }

   } while(false);

   return *this;
}


/**
 *   @brief Tokenize a string
 *
 *   Call this function repeatedly to get tokenized string
 *   until nStart becomes -1.
 *
 *   @param[in] pszTokens -- string containing token delimiters
 *   @param[in,out] nStart -- zero-based index to start searching.
 *                            When the end of string is reached, -1
 *                            will be set to this variable.
 *   
 *   @return string segment (a new copy) containing a token.
 *
 */
CFString CFString::Tokenize(pcwstr_t pszTokens, int& nStart) const
{
   // checking
   if (!pszTokens || nStart >= m_nLen) {
      nStart = -1;
      return CFString(static_cast<void *>(NULL));
   }
 
   // checking
   if (!m_szString || m_nLen == 0) {
      nStart = -1;
      return CFString(static_cast<void *>(NULL));
   }

   int i, tStart, nCount = 0;
   wc_t ch;
   bool isDelimitFound;
   int nrDelimiters = static_cast<int>(CFString::WStrlen(pszTokens));
   

   // adjust parameter
   if (nStart < 0)
      nStart = 0;

   // start parsing our string
   tStart = nStart;
   while (nStart < m_nLen) {
      ch = m_szString[nStart];
      isDelimitFound = false;

      // find delimiters
      for (i = 0 ; i < nrDelimiters; ++i) {
          if (ch == pszTokens[i]) {
             isDelimitFound = true;
             break;
          }
      }

      ++nStart;

      if (isDelimitFound) {
         // skips leading delimiters
         if (tStart == (nStart - 1)) {
            tStart = nStart;
         }
         else {
            // move back one character
            --nStart;
            // break the loop
            break;
         }
      }      
      
   }

   nCount = nStart - tStart;

   // update "start"
   if (nStart >= m_nLen)
      nStart = -1;
   else
      ++nStart;  // move to next charater after the ending delimiter

   if (nCount <= 0)
      return CFString(static_cast<void *>(NULL));   

   return CFString(static_cast<const void*>(GetSubString(tStart, nCount)));
}



/**
 *  @brief Internal use only
 *
 *  Trim all characters on the left occurred in "pszTargets".
 *
 *  @param[in] pszTargets -- string that contains the characters to trim
 */
void CFString::TrimLeftChars(pcwstr_t pszTargets)
{   
   if (!m_szString || m_nLen == 0)
      return;
      
   pwstr_t pStart, pTrimStart;
   wc_t ch;
   int nrTrimChars, i;
   bool hasTrimmed = true;

   nrTrimChars = static_cast<int>(CFString::WStrlen(pszTargets));
   pStart = pTrimStart = m_szString;
      
   // skip all trimmed characters
   while (pTrimStart < m_szString + m_nLen && hasTrimmed) {
       ch = *pTrimStart;

       hasTrimmed = false;
       for (i = 0; i < nrTrimChars; ++i) {
          if (ch == pszTargets[i]) {
             hasTrimmed = true;
             ++pTrimStart;
             break;
          }
       }              
   }

   // shift all charaters to the front
   if (pTrimStart > pStart) {
      while (pTrimStart <= m_szString + m_nLen) {
         *pStart++ = *pTrimStart++;
      }

      // update length
      m_nLen = static_cast<int>(pStart - m_szString) - 1;
   }

}

/**
 *  @brief Internal use only
 *
 *  Trim all characters on the right occurred in "pszTargets".
 *
 *  @param[in] pszTargets -- string that contains the characters to trim
 */
void CFString::TrimRightChars(pcwstr_t pszTargets)
{
   if (!m_szString || m_nLen == 0)
      return;
      
   pwstr_t pEnd;
   wc_t ch;
   int nrTrimChars, i;
   bool hasTrimmed = true;

   nrTrimChars = static_cast<int>(CFString::WStrlen(pszTargets));
   pEnd = m_szString + m_nLen - 1;

   // skip all trimmed characters
   while (pEnd >= m_szString && hasTrimmed) {
       ch = *pEnd;

       hasTrimmed = false;
       for (i = 0; i < nrTrimChars; ++i) {
          if (ch == pszTargets[i]) {
             hasTrimmed = true;
             --pEnd;
             break;
          }
       }              
   }

   if (pEnd < m_szString + m_nLen - 1) {
      // mark the end of string
      ++pEnd;
      *pEnd = _S('\0');

      // update length
      m_nLen = static_cast<int>(pEnd - m_szString);
   }

}

/**
 *  Trim all leading and trailing occurences of a character
 *  
 *  @param[in] chTarget -- character to trim
 *
 *  @return reference to this string
 */
CFString& CFString::Trim(wc_t chTarget)
{
   wc_t szTemp[2] = { chTarget, _S('\0')};
   //
   //  Trim right first so that trimming left need to shift
   //+ less characters
   //
   TrimRightChars(szTemp);
   TrimLeftChars(szTemp);
   return *this;
}

/**
 *  Trim all leading and trailing occurences of all characters
 *  appeared in the string passed in.
 *
 *  @param[in] pszTargets -- string that contains the characters to trim
 *
 *  @return reference to this string
 */
CFString& CFString::Trim(pcwstr_t pszTargets)
{
   TrimRightChars(pszTargets);
   TrimLeftChars(pszTargets);
   return *this;
}


//! whitespaces, newlines and tabs
#define WHITE_STRING _S(" \r\n\t")

/**
 *  Trim all leading and trailing occurences of whitespaces, newlines and tabs
 *
 *  @return reference to this string
 */
CFString& CFString::Trim( )
{
   TrimRightChars(WHITE_STRING);
   TrimLeftChars(WHITE_STRING);
   return *this;
}

/**
 *  Trim all leading occurences of a character
 *
 *  @param[in] chTarget -- character to trim
 *
 *  @return reference to this string
 */
CFString& CFString::TrimLeft(wc_t chTarget)
{
   wc_t szTemp[2] = { chTarget, _S('\0')};
   TrimLeftChars(szTemp);
   return *this;
}

/**
 *  Trim all leading occurences of characters in a string
 *
 *  @param[in] pszTargets -- string containing characters to trim
 *
 *  @return reference to this string
 */
CFString& CFString::TrimLeft(pcwstr_t pszTargets)
{
   TrimLeftChars(pszTargets);
   return *this;
}

/**
 *  Trim all leading occurences of whitespaces, newlines and tabs
 *
 *  @return reference to this string
 */
CFString& CFString::TrimLeft( )
{   
   TrimLeftChars(WHITE_STRING);   
   return *this;
}

/**
 *  Trim all trailing occurences of a character
 *
 *  @param[in] chTarget -- character to trim
 *
 *  @return reference to this string
 */
CFString& CFString::TrimRight(wc_t chTarget)
{
   wc_t szTemp[2] = { chTarget, _S('\0')};
   TrimRightChars(szTemp);
   return *this;
}

/**
 *  Trim all trailing occurences of characters in a string
 *
 *  @param[in] pszTargets -- string containing characters to trim
 *
 *  @return reference to this string
 */
CFString& CFString::TrimRight(pcwstr_t pszTargets)
{
   TrimRightChars(pszTargets);
   return *this;
}

/**
 *  Trim all trailing occurences of whitespaces, newlines and tabs
 *
 *  @return reference to this string
 */
CFString& CFString::TrimRight( )
{
   TrimRightChars(WHITE_STRING);
   return *this;
}

/**
 *  Test if this string is empty
 *
 *  @return true, it is empty
 *  @return false, something inside
 */
bool CFString::IsEmpty() const
{
   return (m_szString == NULL || m_nLen == 0);
}

/**
 *  Get the length of this string
 *
 *  @return length (not including trailing NULL)
 */
int CFString::GetLength() const
{
   return m_nLen;
}

/**
 *  @brief Set the new length of string 
 *
 *  @note It can exceed the orginal length but not the actual buffer size.
 *  This is made public just for convenience so call this at your own risk !
 *
 *  @note After setting the new length, a trailing NULL will *NOT* set for you !
 *
 *  @param[in] len -- new length
 *
 *  @return old length
 */
int CFString::SetLength(int len)
{
   int oldLen = m_nLen;

   if (len < 0) len = 0;
   if (len > m_nSize) len = m_nSize;
     
   m_nLen = len;

   return oldLen;
}

/**
 *  Get the underlying capacity of the buffer
 */
int CFString::GetSize() const
{
   return m_nSize;
}

/**
 *  Get a character at a specified index
 *
 *  @param[in] index -- zero-based index to get
 *
 *  @return character at the specified index
 */
wc_t CFString::GetAt(int index) const
{
   if (!m_szString || index < 0 || index >= m_nLen) 
      throw exceptions::OutOfBoundsException(_S("String index out of bound"), -1);

   return m_szString[index];
}

/**
 *  Set a character at a specified index
 *
 *  @param[in] index -- zero-based index to set
 *  @param[in] ch -- character to set
 * 
 */
void CFString::SetAt(int index, wc_t ch)
{
   if (!m_szString || index < 0 || index >= m_nLen) 
      throw exceptions::OutOfBoundsException(_S("String index out of bound"), -1); 

   m_szString[index] = ch;      
}

/**
 *  Convert internal string to a utf-8 encoded string
 *
 *  @param[out] buf -- pointer to receive the encoded string 
 *                     (will be null-terminated by this function)
 *                     (already added to current auto-release pool)
 *  @param[out] nrChars -- to receive the number of characters in
 *                         the converted buffer.
 *                         Pass a NULL if no interest.
 *
 *  @note if internal string is already NULL, pointer
 *        returned is also NULL.
 *
 *  @return true -- convert OK
 *  @return false -- not OK
 */
bool CFString::ToUtf8String(char ** buf, int * nrChars) const
{
   return ConvertToUtf8String(m_szString, m_nLen, buf, nrChars);
}

/**
 *  User provide a buffer for conversion.
 *
 *  @param[in,out] buf -- buffer provided by user (Pass NULL to get converted length only)
 *  @param[in] nrChars -- size of buffer          (Pass 0 to get converted length only)
 *  @param[out] length -- to receive the length of the converted string (NOT counting terminating null).
 *                        Pass NULL if no interest.
 *
 *  @note buffer size must be at least 2 (so that it can contain
 *        the NULL character at the end)
 *        If buffer is not large enough, it will still be null-terminated.
 *
 *  @return true -- convert OK
 *  @return false -- not OK
 */
bool CFString::ToUtf8String(char * buf, int nrChars, int * length) const
{
    bool ret = false;
    int nrConverted = 0;

    do {
       if (!m_szString || m_nLen == 0) 
          break;

       // buffer too small
       if (buf && nrChars < 2)
          break;

       // actual conversion here
       nrConverted = cppflib::text::UTF16ToUTF8(m_szString, m_nLen, buf, nrChars - 1);

       ret = true;

    } while(false);

    // mark end of string
    if (buf) buf[nrConverted] = '\0';
    // update length 
    if (length) *length = nrConverted;

    return ret;
}

/**
 *  Return a buffer containing the UTF-8 version of this string (null-terminated)
 */
char * CFString::ToUtf8String(void) const
{
    char * buf = NULL;
    ToUtf8String(&buf, NULL);
    return buf;
}


/**
 *  Test if equal
 */
bool CFString::Equals(const BaseObject &obj) const
{
    if (!this->EqualType(obj))
       return false;

    const CFString *pStr = static_cast<const CFString*>(&obj);
    return (this->Compare(*pStr) == 0);
}

/**
 *  Return a hash code
 */
u32_t CFString::GetHash(void) const
{
    u32_t h = 0;
   
    if (!IsEmpty()) {
       u8_t * pData = reinterpret_cast<u8_t*>(this->m_szString);
       int byteLen = this->m_nLen * sizeof(wc_t);
       for (int i = 0; i < byteLen; ++i) {
          u32_t htemp = *pData++ & static_cast<u32_t>(0xff);
          h ^= (htemp << (((i + htemp) * 79) % 23));
       }
    }

    return h;
}

/**
 *  Return string itself
 */
pcwstr_t CFString::ToString(void) const
{
   if (!IsEmpty())
      return static_cast<pcwstr_t>(this->m_szString);

   return BaseObject::ToString();
}


/**
 *  @brief Internal use only
 *
 *  Attach a string directly without allocating extra memory.
 *
 *  @param[in] lpsz -- string to be attached
 */
void CFString::Attach(pwstr_t lpsz)
{
   // free string first if any
   if (m_szString)
      ::MRP_Release(m_szString);

   // assign string
   m_szString = lpsz;
   if (m_szString) {
      __RetStrLenAndSize(m_szString, &m_nLen, &m_nSize);
   }
   else {
      // assign length and size
      m_nLen = 0;
      m_nSize = 0;
   }
}

/**
 *  @brief Internal use only
 *
 *  Attach a string directly without allocating extra memory.
 *  
 *  @param[in] stringSrc -- string to be attached
 */
void CFString::Attach(const CFString &stringSrc)
{
   CFString * pString = const_cast<CFString*>(&stringSrc);

   // assign length first
   m_nLen = pString->GetLength();
   m_nSize = pString->m_nSize;

   // free original string if any
   if (m_szString)
      ::MRP_Release(m_szString);

   m_szString = pString->Detach();
}

/**
 *  @brief Internal use only
 *
 *  Detach our internal string
 */
pwstr_t CFString::Detach(void)
{
   pwstr_t szTemp = m_szString;

   m_szString = NULL;
   m_nLen = 0;
   m_nSize = 0;

   return szTemp;
}

/*************************** operator overloading **************************/

/**
 *  @brief Overload the [] operator
 *
 *  The string can then be accessed like "ch = szString[3]". Internally,
 *  it will call "GetAt".
 *
 *  @param[in] nIndex -- index to get
 *
 *  @return character at the index
 */
wc_t CFString::operator[](int nIndex) const
{   
   return GetAt(nIndex);
}

/**
 *  To access the internal string directly
 *
 *  @return pointer to the internal string
 */
CFString::operator pcwstr_t() const
{
   return this->m_szString;
}



/***************
 * assignments
 ***************/

/**
 *  Assignment with a reference to CFString 
 */
const CFString& CFString::operator=(const CFString& stringSrc)
{   
   if (IS_INTERNAL_STRING(stringSrc.m_attrib)) {
      Attach(stringSrc);
   }
   else {
      InitUTF16ToWideString(static_cast<pcwstr_t>(stringSrc), stringSrc.GetLength(), 0);
   }

   return *this;
}

/**
 *  Assignment with a wide character
 */
const CFString& CFString::operator=(wc_t ch)
{
   wc_t temp[2] = {ch, _S('\0')};   
   InitUTF16ToWideString(temp, CFString::WStrlen(temp), 0);
   
   return *this;
}

/**
 *  Assignment with a UTF-8 string
 */
const CFString& CFString::operator=(const char * psz)
{
   int len = static_cast<int>(CFString::Strlen(psz));
   InitUTF8ToWideString(psz, len, 0);

   return *this;
}

/**
 *  Assignment with a wide character string 
 */
const CFString& CFString::operator=(pcwstr_t lpsz)
{
   int len = static_cast<int>(CFString::WStrlen(lpsz));
   InitUTF16ToWideString(static_cast<pcwstr_t>(lpsz), len, 0);

   return *this;
}


/*******************************
 * concatenate -- append
 *******************************/

/**
 *  Concatenate with a reference to CFString
 */
const CFString& CFString::operator+=(const CFString& string)
{

   if (!m_szString && IS_INTERNAL_STRING(string.m_attrib)) {
      //
      // if myself is already NULL, do a direct attach.
      //
      Attach(string);
   }
   else {
      InitUTF16ToWideString(static_cast<pcwstr_t>(string), string.GetLength(), 0, true);
   }
      
   return *this;
}

/**
 *  Concatenate with a wide character
 */
const CFString& CFString::operator+=(wc_t ch)
{
   wc_t temp[2] = {ch, _S('\0')};
   InitUTF16ToWideString(temp, CFString::WStrlen(temp), 0, true);
   return *this;
}

/**
 *  Concatenate with a UTF-8 string
 */
const CFString& CFString::operator+=(const char * psz)
{
   int len = static_cast<int>(CFString::Strlen(psz));
   InitUTF8ToWideString(psz, len, 0, true);
   return *this;
}

/**
 *  Concatenate with a wide character string
 */
const CFString& CFString::operator+=(pcwstr_t lpsz)
{
   int len = static_cast<int>(CFString::WStrlen(lpsz));
   InitUTF16ToWideString(lpsz, len, 0, true);
   return *this;
}

/**
 *  @brief Internal use only
 *
 *  Concatenate with a wide character string
 */
void CFString::ConcatString(pcwstr_t string, int len)
{
   //  append string   
   //+ Allocate more space here to make sure there will be enough space
   //+ to append length and size.
   InitUTF16ToWideString(string, len, __GetStrLenReserved(m_nLen + len), true);      

   //  append length and size at the end
   __WriteStrLenAndSize(this);
}

/**
 *  @brief Internal use only
 *
 *  Concatenate with a UTF-8 string
 */
void CFString::ConcatString(const char * string, int len)
{
   //  append string   
   //+ Allocate more space here to make sure there will be enough space
   //+ to append length and size.
   InitUTF8ToWideString(string, len, __GetStrLenReserved(m_nLen + len), true);      

   //  append length and size at the end
   __WriteStrLenAndSize(this);
}

/************** public string functions *********************/

bool CFString::IsNullOrEmpty(CFString *string)
{
   if (string == NULL)
      return true;

   return string->IsEmpty();
}

/**
 *  Get a empty string
 */
CFString CFString::Empty()
{
   return CFString(); // this won't allocate memory, which is very efficient
}

/**
 *  A convenient function to make a string using 'Format'
 */
CFString CFString::SFormat(pcwstr_t pszFormat, ...)
{
   CFString s;
   s.__InternalFormat(&pszFormat, 0);
   return s;
}

/**
 *  Count number of 8-bit chars
 *
 *  @return 0 if lpsz is NULL
 */
sz_t CFString::Strlen(const char *lpsz)
{
   sz_t len = 0;
   
   if (lpsz) {
      while (*lpsz++)
         ++len;
   }

   return len;
}

/**
 *  To count number of wide chars (surrogate pairs is 2)
 *
 *  @return 0 if lpsz is NULL
 */
sz_t CFString::WStrlen(pcwstr_t lpsz)
{
   sz_t len = 0;
   
   if (lpsz) {
      while (*lpsz++)
         ++len;
   }

   return len;
}

/**
 *  wcscmp
 */
int CFString::WStrcmp(pcwstr_t psz1, pcwstr_t psz2)
{
   wc_t c1, c2;

   if (!psz1 && !psz2) return 0;
   else if (!psz1) return -1;
   else if (!psz2) return 1;
   
   while (true) {
      c1 = *psz1++;
      c2 = *psz2++;
      if (c1 > c2) return 1;
      else if (c1 < c2) return -1;
      
      if (c1 == 0 || c2 == 0)
         break;
   }

   return 0;
}

/**
 *  wcsncmp (with length specified)
 */
int CFString::WStrncmp(pcwstr_t psz1, pcwstr_t psz2, sz_t len)
{
   sz_t i;
   wc_t c1, c2;
   
   if (!psz1 && !psz2) return 0;
   else if (!psz1) return -1;
   else if (!psz2) return 1;
   
   for (i = 0; i < len; ++i) {
      c1 = *psz1++;
      c2 = *psz2++;
      if (c1 > c2) return 1;
      else if (c1 < c2) return -1;
   }

   return 0;
}

/**
 *  wcsicmp (ignore case)
 */
int CFString::WStricmp(pcwstr_t psz1, pcwstr_t psz2)
{
   wc_t c1, c2;

   if (!psz1 && !psz2) return 0;
   else if (!psz1) return -1;
   else if (!psz2) return 1;
   
   while (true) {
      c1 = *psz1++;
      c2 = *psz2++;
      // convert to lower case first
      if (c1 >= _S('A') && c1 <= _S('Z')) c1 += (_S('a') - _S('A'));
      if (c2 >= _S('A') && c2 <= _S('Z')) c2 += (_S('a') - _S('A'));

      if (c1 > c2) return 1;
      else if (c1 < c2) return -1;
      
      if (c1 == 0 || c2 == 0)
         break;
   }

   return 0;
}

/**
 *  wcsicmp (ignore case) with length specified
 */
int CFString::WStrnicmp(pcwstr_t psz1, pcwstr_t psz2, sz_t len)
{
   wc_t c1, c2;

   if (!psz1 && !psz2) return 0;
   else if (!psz1) return -1;
   else if (!psz2) return 1;
   
   for (sz_t i = 0; i < len; ++i) {
      c1 = *psz1++;
      c2 = *psz2++;
      // convert to lower case first
      if (c1 >= _S('A') && c1 <= _S('Z')) c1 += (_S('a') - _S('A'));
      if (c2 >= _S('A') && c2 <= _S('Z')) c2 += (_S('a') - _S('A'));

      if (c1 > c2) return 1;
      else if (c1 < c2) return -1;
   }

   return 0;
}

/**
 *  memcpy 
 *
 *  @param [in] size -- number of bytes
 *
 *  @return pDest
 */
void *CFString::Memcpy(void *pDest, const void *pSrc, sz_t size)
{
   u8_t * pD = static_cast<u8_t*>(pDest);
   u8_t * pS = static_cast<u8_t*>(const_cast<void*>(pSrc));
   u8_t * pDEnd = pD + size - 1;
   u8_t * pSEnd = pS + size - 1;

   if (pD == pS || size == 0)
      return pDest;

   if (pS > pD || pSEnd < pD) {

      for (sz_t i = 0 ; i < size; ++i) 
         *pD++ = *pS++;
   }
   else {
      /*
       * copy from end to head since source buffer overlapped with dest buffer like this:
       *
       *
       *   pS           pD         pSEnd       pDEnd
       *   |            |           |            |
       *  \|/          \|/         \|/          \|/
       *   +------------+-----------+------------+
       *   |            |           |            |
       *   +------------+-----------+------------+
       *
       */
      for (sz_t i = 0 ; i < size; ++i) 
         *pDEnd-- = *pSEnd--;
   }
   
   return pDest;
}

/**
 *  memset
 */
void *CFString::Memset(void *pDest, int ch, sz_t size)
{
   sz_t i;
   u8_t * p1 = static_cast<u8_t*>(pDest);   
   u8_t c8 = static_cast<u8_t>(ch);

   for (i = 0 ; i < size; ++i) 
      *p1++ = c8;
   
   return pDest;
}

/**
 *  Convenient function to convert from UTF16 to UTF8
 *
 *  @param [out] buf -- buffer to receive the UTF8 string
 *  @param [out] nrChars -- (optional) number of UTF8 characters converted
 */
bool CFString::ConvertToUtf8String(pcwstr_t src, int srcLen, char ** buf, int * nrChars)
{
   if (!src || srcLen <= 0) {
      *buf = NULL;
      if (nrChars) *nrChars = 0;
      return false;
   }

   // check buffer needed
   int charNeeded = cppflib::text::UTF16ToUTF8(src, srcLen, NULL, 0);
   *buf = new (cppmempool::NEWPTR) char[charNeeded + 1]; // allocate and add to current mem pool
   if (!(*buf))
      return false;
   
   // actual conversion here
   cppflib::text::UTF16ToUTF8(src, srcLen, *buf, charNeeded);
   
   // mark end of string
   (*buf)[charNeeded] = '\0';
   // update length 
   if (nrChars) *nrChars = charNeeded;

   return true;
}

char *CFString::ConvertToUtf8String(pcwstr_t src, int srcLen)
{
   char *buf = NULL;
   ConvertToUtf8String(src, srcLen, &buf, NULL);
   return buf;
}

char *CFString::ConvertToUtf8String(pcwstr_t src)
{
   char *buf = NULL;
   int srcLen = static_cast<int>(CFString::WStrlen(src));
   ConvertToUtf8String(src, srcLen, &buf, NULL);
   return buf;
}


/**************************************** 
 *  operator '+' between 2 strings. 
 *  These are actually global functions.
 ****************************************/

/**
 *  @brief Concatenate 2 CFString
 *
 *  For example, we want to execute 
 *            a = b + c + d;
 *
 *                                    produce
 *  Actually, "b + c" will do first     =>    temp (a CFString)
 *                                    produce
 *      then  "temp + d" will be next   =>    temp2 and at last
 *
 *            "a = temp2"
 *
 *  The above was checked from debug messages in all those constructors,
 *  overloaded functions and destructors. There will be many intermediate
 *  CFString objects produced if there are many "+" in between. Thus, 
 *  some tricky code is written to avoid wasting memory. (See "return")
 *
 *
 *  @param[in] string1 -- first string to do concatenation
 *  @param[in] string2 -- second string to do concatenation
 *
 *  @return Upon returning, a new instance of "CFString" is contructed.
 *          In order to save memory, "Detach" is called and cast it to
 *          "const void *" so that our private constructor is called to attach
 *          the detached string directly.
 *          By using this "detach and attach" scheme, the same piece of memory
 *          will be passed from one temp "CFString" to another temp 
 *          "CFString" without copying so many intermediate string if there
 *          are so many "+"s in the expression.
 *          Length and size information are appended at the end of the normal
 *          string.
 */
_DLLAPI CFString operator+(const CFString& string1, const CFString& string2)
{   
   int sizeOfString = __GetStrLenReserved(string1.GetLength() + string2.GetLength());

   if (IS_INTERNAL_STRING(string1.m_attrib)) {
      CFString * pTempString = const_cast<CFString *>(&string1);            
      // concatenate string
      pTempString->ConcatString(static_cast<pcwstr_t>(string2), string2.GetLength());      
      //  cast to "const void*" so that our private constructor 
      //+ will be called
      return CFString(static_cast<const void*>(pTempString->Detach()));
   }
   else if (IS_INTERNAL_STRING(string2.m_attrib)) {      
      CFString * pTempString = const_cast<CFString *>(&string2); 
      // insert string           
      pTempString->InsertWriteLenAndSize(0, static_cast<pcwstr_t>(string1), sizeOfString);
      //  cast to "const void*" so that our private constructor 
      //+ will be called
      return CFString(static_cast<const void*>(pTempString->Detach()));
   }
   else {
      //
      // string1 and string2 are not internal (temporary) strings,
      // we can't use it. Create a new one instead.
      //         
      CFString szTemp(static_cast<pcwstr_t>(string1), string1.GetLength(), sizeOfString);      

      // concatenate the string
      szTemp.ConcatString(static_cast<pcwstr_t>(string2), string2.GetLength());
      
      //  cast to "const void*" so that our private constructor 
      //+ will be called
      return CFString(static_cast<const void*>(szTemp.Detach()));
   }
   
   // we won't come to here actually
   return CFString(static_cast<const void*>(NULL));
}

/**
 *  Concatenate a CFString and a wide character
 */
_DLLAPI CFString operator+(const CFString& string, wc_t ch)
{
   wc_t string2[2] = {ch, _S('\0')};

   if (IS_INTERNAL_STRING(string.m_attrib)) {      
      CFString * pTempString = const_cast<CFString *>(&string);
      // concatenate string
      pTempString->ConcatString(static_cast<pcwstr_t>(string2), 1); 
      return CFString(static_cast<const void*>(pTempString->Detach()));           
   }
   else {
      int sizeOfString = __GetStrLenReserved(string.GetLength() + 1);
      CFString szTemp(static_cast<pcwstr_t>(string), string.GetLength(), sizeOfString);      
      // concatenate the string
      szTemp.ConcatString(static_cast<pcwstr_t>(string2), 1);      
      return CFString(static_cast<const void*>(szTemp.Detach()));   
   }

   // we won't come to here actually
   return CFString(static_cast<const void*>(NULL));
}

/**
 *  Concatenate a wide character and a CFString
 */
_DLLAPI CFString operator+(wc_t ch, const CFString& string)
{
   wc_t string2[2] = {ch, _S('\0')};
   int sizeOfString = __GetStrLenReserved(string.GetLength() + 1);

   if (IS_INTERNAL_STRING(string.m_attrib)) {
      CFString * pTempString = const_cast<CFString *>(&string); 
      // insert string  
      pTempString->InsertWriteLenAndSize(0, static_cast<pcwstr_t>(string2), sizeOfString);
      return CFString(static_cast<const void*>(pTempString->Detach()));
   }
   else {
      CFString szTemp(static_cast<pcwstr_t>(string2), 1, sizeOfString);      
      // concatenate the string
      szTemp.ConcatString(static_cast<pcwstr_t>(string), string.GetLength());      
      return CFString(static_cast<const void*>(szTemp.Detach()));
   }

   // we won't come to here actually
   return CFString(static_cast<const void*>(NULL));
}

/**
 *  Concatenate a CFString and an ASCII character
 */
_DLLAPI CFString operator+(const CFString& string, char ch)
{
   char string2[2] = {ch, '\0'};

   if (IS_INTERNAL_STRING(string.m_attrib)) {      
      CFString * pTempString = const_cast<CFString *>(&string);
      // concatentat string
      pTempString->ConcatString(static_cast<const char*>(string2), 1); 
      return CFString(static_cast<const void*>(pTempString->Detach()));         
   }
   else {
      int sizeOfString = __GetStrLenReserved(string.GetLength() + 1);
      CFString szTemp(static_cast<pcwstr_t>(string), string.GetLength(), sizeOfString);      
      // concatenate the string
      szTemp.ConcatString(static_cast<const char*>(string2), 1);      
      return CFString(static_cast<const void*>(szTemp.Detach()));
   }

   // we won't come to here actually
   return CFString(static_cast<const void*>(NULL));
}

/**
 *  Concatenate an ASCII character and a CFString
 */
_DLLAPI CFString operator+(char ch, const CFString& string)
{
   wc_t string2[2] = {static_cast<wc_t>(ch), _S('\0')};
   int sizeOfString = __GetStrLenReserved(string.GetLength() + 1);

   if (IS_INTERNAL_STRING(string.m_attrib)) {
      CFString * pTempString = const_cast<CFString *>(&string);
      // insert string
      pTempString->InsertWriteLenAndSize(0, static_cast<pcwstr_t>(string2), sizeOfString);
      return CFString(static_cast<const void*>(pTempString->Detach()));
   }
   else {
      CFString szTemp(static_cast<pcwstr_t>(string2), 1, sizeOfString);
      // concatenate the string
      szTemp.ConcatString(static_cast<pcwstr_t>(string), string.GetLength());      
      return CFString(static_cast<const void*>(szTemp.Detach()));
   }

   // we won't come to here actually
   return CFString(static_cast<const void*>(NULL));
}

/**
 *  Concatenate a CFString and a wide character string
 */
_DLLAPI CFString operator+(const CFString& string, pcwstr_t lpsz)
{
   int lpszLen = static_cast<int>(CFString::WStrlen(lpsz));

   if (IS_INTERNAL_STRING(string.m_attrib)) {      
      CFString * pTempString = const_cast<CFString *>(&string);
      // concatenate string
      pTempString->ConcatString(static_cast<pcwstr_t>(lpsz), lpszLen); 
      return CFString(static_cast<const void*>(pTempString->Detach()));          
   }
   else {
      int sizeOfString = __GetStrLenReserved(string.GetLength() + lpszLen);
      CFString szTemp(static_cast<pcwstr_t>(string), string.GetLength(), sizeOfString);
      // concatenate the string
      szTemp.ConcatString(static_cast<pcwstr_t>(lpsz), lpszLen);      
      return CFString(static_cast<const void*>(szTemp.Detach()));
   }

   // we won't come to here actually
   return CFString(static_cast<const void*>(NULL));
}

/**
 *  Concatenate a wide character string and a CFString
 */
_DLLAPI CFString operator+(pcwstr_t lpsz, const CFString& string)
{
   int lpszLen = static_cast<int>(CFString::WStrlen(lpsz));
   int sizeOfString = __GetStrLenReserved(string.GetLength() + lpszLen);

   if (IS_INTERNAL_STRING(string.m_attrib)) {   
      CFString * pTempString = const_cast<CFString *>(&string);
      // insert string
      pTempString->InsertWriteLenAndSize(0, static_cast<pcwstr_t>(lpsz), sizeOfString);
      return CFString(static_cast<const void*>(pTempString->Detach()));
   }
   else {
      CFString szTemp(lpsz, lpszLen, sizeOfString);
      // concatenate the string
      szTemp.ConcatString(static_cast<pcwstr_t>(string), string.GetLength());      
      return CFString(static_cast<const void*>(szTemp.Detach()));
   }

   // we won't come to here actually
   return CFString(static_cast<const void*>(NULL));
}

/**
 *  Concatenate a CFString and a UTF-8 character string
 */
_DLLAPI CFString operator+(const CFString& string, const char * psz)
{
   int pszLen = static_cast<int>(CFString::Strlen(psz));

   if (IS_INTERNAL_STRING(string.m_attrib)) {      
      CFString * pTempString = const_cast<CFString *>(&string);
      // concatenate string
      pTempString->ConcatString(psz, pszLen);
      return CFString(static_cast<const void*>(pTempString->Detach()));
   }
   else {
      int sizeOfString = __GetStrLenReserved(string.GetLength() + pszLen);
      CFString szTemp(static_cast<pcwstr_t>(string), string.GetLength(), sizeOfString);
      // concatenate the string
      szTemp.ConcatString(psz, pszLen);      
      return CFString(static_cast<const void*>(szTemp.Detach()));
   }

   // we won't come to here actually
   return CFString(static_cast<const void*>(NULL));
}

/**
 *  Concatenate a UTF-8 string and a CFString
 */
_DLLAPI CFString operator+(const char * psz, const CFString& string)
{
   int pszLen = static_cast<int>(CFString::Strlen(psz));
   int sizeOfString = __GetStrLenReserved(string.GetLength() + pszLen);

   if (IS_INTERNAL_STRING(string.m_attrib)) {
      CFString * pTempString = const_cast<CFString *>(&string);
      // insert string
      pTempString->InsertWriteLenAndSize(0, static_cast<const char*>(psz), sizeOfString);
      return CFString(static_cast<const void*>(pTempString->Detach()));
   }
   else {
      CFString szTemp(static_cast<const char*>(psz), pszLen, sizeOfString);
      // concatenate the string
      szTemp.ConcatString(static_cast<pcwstr_t>(string), string.GetLength());
      return CFString(static_cast<const void*>(szTemp.Detach()));
   }

   return CFString(static_cast<const void*>(NULL));
}



/**************************************** 
 *  operator ==, !=, <, >, <=, >=
 *  These are actually global functions
 *  and just call CFString::Compare
 ****************************************/

/**
 *  @brief Test equalitly between a CFString with another CFString
 *
 *  Internally, the "Compare" member function will be called
 */
_DLLAPI bool operator==(const CFString& s1, const CFString& s2)
{
   return (s1.Compare(s2) == 0);   
}

/**
 *  Test equalitly between a CFString with a wide character string
 */
_DLLAPI bool operator==(const CFString& s1, pcwstr_t s2)
{
   return (s1.Compare(s2) == 0);
}

/**
 *  Test equalitly between a wide character string with a CFString
 */
_DLLAPI bool operator==(pcwstr_t s1, const CFString& s2)
{
   return (s2.Compare(s1) == 0);
}

/**
 *  Test inequalitly between a CFString with another CFString 
 */
_DLLAPI bool operator!=(const CFString& s1, const CFString& s2)
{
   return (s1.Compare(s2) != 0);
}

/**
 *  Test inequalitly between a CFString with a wide character string 
 */
_DLLAPI bool operator!=(const CFString& s1, pcwstr_t s2)
{
   return (s1.Compare(s2) != 0);
}

/**
 *  Test inequalitly between a wide character string with a CFString
 */
_DLLAPI bool operator!=(pcwstr_t s1, const CFString& s2)
{
   return (s2.Compare(s1) != 0);
}

/**
 *  Test "less than" between a CFString with another CFString 
 */
_DLLAPI bool operator<(const CFString& s1, const CFString& s2)
{
   return (s1.Compare(s2) < 0);
}

/**
 *  Test "less than" between a CFString with a wide character string
 */
_DLLAPI bool operator<(const CFString& s1, pcwstr_t s2)
{
   return (s1.Compare(s2) < 0);
}

/**
 *  Test "less than" between a wide character string with a CFString
 */
_DLLAPI bool operator<(pcwstr_t s1, const CFString& s2)
{
   return (s2.Compare(s1) > 0);
}

/**
 *  Test "greater than" between a CFString with another CFString 
 */
_DLLAPI bool operator>(const CFString& s1, const CFString& s2)
{
   return (s1.Compare(s2) > 0);            
}

/**
 *  Test "greater than" between a CFString with a wide character string
 */
_DLLAPI bool operator>(const CFString& s1, pcwstr_t s2)
{
   return (s1.Compare(s2) > 0);            
}

/**
 *  Test "greater than" between a wide character string with a CFString
 */
_DLLAPI bool operator>(pcwstr_t s1, const CFString& s2)
{
   return (s2.Compare(s1) < 0);            
}

/**
 *  Test "less than or equal to" between a CFString with another CFString 
 */
_DLLAPI bool operator<=(const CFString& s1, const CFString& s2)
{
   return (s1.Compare(s2) <= 0);            
}

/**
 *  Test "less than or equal to" between a CFString with a wide character string
 */
_DLLAPI bool operator<=(const CFString& s1, pcwstr_t s2)
{
   return (s1.Compare(s2) <= 0);            
}

/**
 *  Test "less than or equal to" between a wide character string with a CFString
 */
_DLLAPI bool operator<=(pcwstr_t s1, const CFString& s2)
{
   return (s2.Compare(s1) >= 0);      
}

/**
 *  Test "greater than or equal to" between a CFString with another CFString 
 */
_DLLAPI bool operator>=(const CFString& s1, const CFString& s2)
{
   return (s1.Compare(s2) >= 0);            
}

/**
 *  Test "greater than or equal to" between a CFString with a wide character string
 */
_DLLAPI bool operator>=(const CFString& s1, pcwstr_t s2)
{
   return (s1.Compare(s2) >= 0);   
}

/**
 *  Test "greater than or equal to" between a wide character string with a CFString 
 */
_DLLAPI bool operator>=(pcwstr_t s1, const CFString& s2)
{
   return (s2.Compare(s1) <= 0);      
}


}  // end of namespace cppflib
